package main;

import java.util.*;


public class Main {

	static int n_data_cache,IBIndex,
	n_data_mem, ib_size,capacity,
	res_stat_num, num_add, num_mul, num_div, num_nand,
	cycles_add, cycles_mul, cycles_div, cycles_nand;
	
	static int ways = 4;
	static final int freeListSize = 32;
	static int PC = 0;
	static int END = 6;
	static final int memory_block_size = 16; //in bytes
	static int reserve = 0;
	static int reserveROB = 0;
	
	//Caches
	static boolean[] writingPolicy_hit = new boolean[3];
	static boolean[] writingPolicy_miss = new boolean[3];
	static int[] S = new int[3];
	static int[] l = new int[3];
	static int[] m = new int[3];
	static int[] C = new int[3];
	static int[] cache_cycles = new int[3];
	static double[] cache_accesses = new double[3];
	static double[] cache_hit = new double[3];
	
	static int ROBSize=32;
	//Decode--rename
	static Integer RegFile [] = new Integer[8];
	static Map<Integer,Integer> RegMapping = new HashMap <Integer, Integer>();
	static Map<Integer,Order> RegRobMapping = new HashMap <Integer, Order>();
	static ArrayList<Integer> usedPhysicalRegisters = new ArrayList<Integer>();
	static int freeList_index = 8;
	
	//Memory
	static int cycles_spanned,n_cache_levels;
	
	static ArrayList<CacheEntry> I_Cache = new ArrayList<CacheEntry>();
	static ArrayList<CacheEntry> D_Cache = new ArrayList<CacheEntry>();
	static ArrayList<CacheEntry> Cache2 = new ArrayList<CacheEntry>();
	static ArrayList<CacheEntry> Cache3 = new ArrayList<CacheEntry>();
	
	static Map<Integer,Integer> DataMemory = new HashMap <Integer, Integer>();
	static Map<Integer,Instruction> IMemory = new HashMap <Integer, Instruction>();
	
	static LinkedList<ROBEntry> ROB = new LinkedList<ROBEntry>();
	
	static LinkedList<Instruction> instructions = new LinkedList<Instruction>();
	static LinkedList<Instruction> inst_buffer = new LinkedList<Instruction>();
	static LinkedList<ResStationEntry> ReservationSation = new LinkedList<ResStationEntry>(); 
	
	static LinkedList<FunctionalUnit> FS = new LinkedList<FunctionalUnit>();
	
	
	static final Stage Fetch = new Stage();
	static final Stage Decode = new Stage();
	static final Stage Dispatch = new Stage();
	static final Stage Issue = new Stage();
	static final Stage Execute = new Stage();
	static final Stage Commit = new Stage();
	
	static void runProcessor() {
		
		boolean d1 = true , d2 = false;
		boolean f1 = true , f2 = false;
		
		for(cycles_spanned=1; !finished(); cycles_spanned++) {
			
			System.out.println("*******************************************");
			System.out.println("Cycle "+(cycles_spanned));
			
			f1 = Stage.executeFetch();
			d1 = Stage.executeDecode(f2); 
			f2 = f1;
			Stage.executeDispatch(d2); 
			d2 = d1;
			Stage.executeIssue();
			Stage.executeExecute();
			Stage.executeCommit();
			
			countCycles();
			printAllStages();
			System.out.println("******************************************");
			System.out.println("Instruction Buffer");
			printIBuffer();
			System.out.println("******************************************");
			System.out.println("Register Mapping");
			System.out.println(RegMapping);
			System.out.println("******************************************");
			System.out.println("Reservation Sations");
			ResStationEntry.print();
			System.out.println("******************************************");
			System.out.println("ROB");
			ROBEntry.print();
			
			System.out.println("******************************************");
			System.out.println("******************************************");
			
		}
		
			finalOutput();
			System.out.println("Start and Finish Times for each instruction");
			for(int m=0; m<instructions.size(); m++){
				Instruction e = instructions.get(m);
				System.out.println("["+e.start+","+e.finish+"]"+e.noOfCycles+";");
			}
	}
	
	private static void printIBuffer() {
		
		for(int j=0; j<inst_buffer.size(); j++) {
			Instruction ins = inst_buffer.get(j);
			System.out.println(ins.format+"  N = "+ins.N + " ; "+ins.dest+","+ins.source1+","+ins.source2);
			
		}
		
	}

	static void finalOutput() {
		
		double ipc = 0.0;
		
		for(int m=0; m<instructions.size(); m++)
			ipc += (instructions.get(m).noOfCycles);
		
		ipc = instructions.size()/ipc;
		
		System.out.println("Number of cycles spanned = "+--cycles_spanned);
		System.out.println("IPC = "+ipc);
		
		for(int i=1; i<=n_cache_levels; i++) {
			System.out.println("Hit ratio of Cache "+i+" = "
					+(cache_hit[i-1]/(cache_accesses[i-1]))*100+"%");
		}
		
		//CacheEntry.PrintALLMemory();
		//System.out.println(cache_accesses[2]+";"+cache_hit[2]);
		
	}
	
	static void init() {
		
		Instruction i1 = new Instruction("MUL", 4, 0, 2, 0, 0,"R4 <- R0*R2");
		Instruction i2 = new Instruction("MUL", 6, 4, 7, 0, 0,"R6 <- R4*R7");
		Instruction i3 = new Instruction("ADD", 7, 4, 3, 0, 0,"R7 <- R4+R3");
		Instruction i4 = new Instruction("ADDI", 4, 5, 0, 80, 0,"R4 <- R5+80");
		Instruction i5 = new Instruction("NAND", 6, 2, 7, 0, 0,"R6 <- R2&~R7");
		Instruction i6 = new Instruction("DIV", 6, 4, 4, 0, 0,"R6 <- R4/R4");
		
//		Instruction i7 = new Instruction("LW", 5, 4, 0, 0, 0,"LW r5,r0,0");
//		Instruction i8 = new Instruction("ADDI", 5, 4, 4, 8, 0,"ADDI r5,r4,8");
//		Instruction i9 = new Instruction("RET", -1, 5, 0, 0, 0,"RET r5");
//		Instruction i10 = new Instruction("SW", -1, 5, 0, 4, 0,"SW r5,r0,4");
//		Instruction i11 = new Instruction("SW", -1, 4, 4, 2, 0,"SW r4,r4,2");
//		Instruction i12 = new Instruction("ADDI", 4, 0, 0, 16, 0,"R4 <- R0+16");
//		Instruction i13 = new Instruction("JALR", 6, 4, 0, 0, 0,"JALR r6,r4");
//		Instruction i14 = new Instruction("ADD", 7, 4, 3, 0, 0,"R7 <- R4+R3");
//		Instruction i15 = new Instruction("ADDI", 4, 7, 0, 50, 0,"R4 <- R7+50");
////		
		Instruction i10 = new Instruction("ADDI", 4, 0, 0, 3, 0,"R4 <- R0+#3");
		Instruction i11 = new Instruction("ADDI", 5, 0, 0, 0, 0,"R5 <- R0+#0");
		Instruction i12 = new Instruction("ADDI", 5, 5, 0, 1, 0,"R5 <- R5+#1");
		Instruction i13 = new Instruction("BEQ", -1, 4, 5, 2, 0,"BEQ r4,r5,#2");
		Instruction i14 = new Instruction("JMP", -1, 0, 0, -6, 0,"JMP r0,#-6");
		Instruction i15 = new Instruction("ADDI", 5, 5, 0, 60, 0,"R5 <- R5+#60");
				
		
		//IMemory.put(0, i1);IMemory.put(1, i2);IMemory.put(2, i3);IMemory.put(3, i4);
		//IMemory.put(4, i5);IMemory.put(5, i6);
		
		//IMemory.put(0, i7);IMemory.put(1, i8);
//		IMemory.put(2, i9);IMemory.put(3, i10);IMemory.put(4, i11);IMemory.put(5, i12);
//		IMemory.put(6, i13);IMemory.put(7, i14);IMemory.put(8, i15);
//		
		IMemory.put(0, i10);IMemory.put(1, i11);IMemory.put(2, i12);
		IMemory.put(3, i13);IMemory.put(4, i14);IMemory.put(5, i15);
		
		
		ib_size = 10;
		res_stat_num = 8;
		IBIndex = 0;
		ROBSize = 10;
		
		for(int i=0; i<RegFile.length; i++) RegFile[i] = 0;
		
		FunctionalUnit f1 = new FunctionalUnit('a', 1);
		FunctionalUnit f5 = new FunctionalUnit('m', 3);
		FunctionalUnit f2 = new FunctionalUnit('m', 3);
		FunctionalUnit f3 = new FunctionalUnit('d', 4);
		FunctionalUnit f4 = new FunctionalUnit('n', 1);
		//FunctionalUnit f3 = new FunctionalUnit('a', 3);
		
		FS.add(f1);
		FS.add(f2);
		FS.add(f3);
		FS.add(f4);
		FS.add(f5);
		
		num_add = num_div = num_mul = num_nand = 1;
		num_mul = 2;
		
		MemoryUnit.initialiazeMemory();
		
		
	}
	
	static void countCycles() {
		
		for(int i=0; i<instructions.size(); i++) {
			
			Instruction ins = instructions.get(i);
			if(!ins.finished) ins.noOfCycles++;
			
		}
		
	}
	
	static boolean finished() {
		
		if(PC == END && !MemoryUnit.beginFetch){ 
		
		boolean f = true;
			
		for(int i=0; i<instructions.size(); i++) f&=instructions.get(i).finished;
		
		return f;
		
		}
		
		return false;
	}
	
	static void printAllStages() {
		
		for(int i=0; i<6; i++) {
			
			Stage s = null;
			
			System.out.println("-------------------------------------");
			
			switch(i) {
			case 0: System.out.println("Fetch Stage"); s = Fetch;break;
			case 1: System.out.println("Decode-Rename Stage"); s = Decode;break;
			case 2: System.out.println("Dispatch Stage"); s = Dispatch;break;
			case 3: System.out.println("Issue Stage"); s = Issue; break;
			case 4: System.out.println("Execute Stage"); s = Execute; break;
			case 5: System.out.println("Commit Stage"); s = Commit; break;
			}
			
			if(i == 4) {Stage.printExecute();}
						
			for(int j=0; j<s.stageInstructions.size(); j++){
				
				Instruction ins =  s.stageInstructions.get(j);
				
				System.out.println(ins.format+"  N = "+ins.N + " ; "+ins.dest+","+ins.source1+","+ins.source2);
			
			}	
			
			System.out.println("-------------------------------------");
			
		}
	}
	
	public static void main(String[] args) {
		
		//init();
		
		try{
			Input.takeInput();
			Input.AssemblyFile();
		}
		catch(Exception e) {
			e.printStackTrace();
			System.out.println("Error in Input");
			return;
		}
		
		runProcessor();
		
	}
	
	
	
	
}
